<template>
	<view class="container">
		<view class="header">
			<text class="title">小智服务器测试页面</text>
		</view>

		<!-- 连接信息 -->
		<view class="section">
			<view class="section-header">
				<text class="section-title">连接信息</text>
				<button class="control-button" @click="toggleConnection">{{ connectButtonText }}</button>
			</view>
		</view>

		<!-- 消息类型标签 -->
		<view class="tabs">
			<button :class="['tab', { active: activeTab === 'text' }]" @click="switchTab('text')">文本消息</button>
			<button :class="['tab', { active: activeTab === 'voice' }]" @click="switchTab('voice')">语音消息</button>
		</view>

		<!-- 文本消息 -->
		<view v-if="activeTab === 'text'" class="section">
			<view class="message-input">
				<input class="message-input-field" v-model="messageInput" :disabled="!isConnected" placeholder="输入消息..."
					@confirm="sendTextMessage" />
				<button class="send-button" @click="sendTextMessage" :disabled="!isConnected">
					发送
				</button>
			</view>
		</view>

		<!-- 语音消息 -->
		<view v-if="activeTab === 'voice'" class="section">
			<view class="audio-controls">
				<button :class="['record-button', { recording: isRecording }]" @click="toggleRecording"
					:disabled="!isConnected">
					{{ isRecording ? `停止录音 ${recordingTime.toFixed(1)}秒` : '开始录音' }}
				</button>
			</view>
			<view class="audio-visualizer">
				<canvas canvas-id="audioVisualizer" class="visualizer-canvas"></canvas>
			</view>
		</view>

		<!-- 会话记录 -->
		<view class="section">
			<text class="section-title">会话记录</text>
			<scroll-view class="content-container" scroll-y>
				<view class="conversation-container">
					<view class="conversation">
						<view v-for="(msg, index) in messages" :key="index"
							:class="['message', msg.isUser ? 'user' : 'server']">
							{{ msg.text }}
						</view>
					</view>
				</view>
				<view class="log-container">
					<view v-for="(log, index) in logs" :key="index" :class="['log-entry', `log-${log.type}`]">
						{{ log.timestamp }} {{ log.message }}
					</view>
				</view>
			</scroll-view>
		</view>
	</view>
</template>

<script setup>
	import {
		ref,
		reactive,
		onMounted,
		computed,
		watch
	} from 'vue'
	import {
		onLoad
	} from '@dcloudio/uni-app'

	// 状态管理
	const activeTab = ref('text')
	const configPanelExpanded = ref(false)
	const isRecording = ref(false)
	const isConnected = ref(false)
	const recordingTime = ref(0)
	const recordingTimer = ref(null)

	// 配置信息
	const deviceMac = ref('00:11:22:33:44:55')
	const deviceName = ref('Web测试设备')
	const clientId = ref('web_test_client')
	const token = ref('your-token1')
	const displayMac = ref('')
	const displayClient = ref('web_test_client')

	// 连接信息
	const otaUrl = ref('https://2662r3426b.vicp.fun/xiaozhi/ota/')
	const serverUrl = ref('wss://2662r3426b.vicp.fun/xiaozhi/v1/')
	const otaStatusText = ref('ota未连接')
	const wsStatusText = ref('ws未连接')
	const connectButtonText = ref('连接')

	// 消息相关
	const messageInput = ref('')
	const messages = ref([])
	const logs = ref([])

	// WebSocket连接
	const websocket = ref(null)

	// 计算属性
	const otaStatusClass = computed(() => {
		if (otaStatusText.value === 'ota已连接') return 'status-success'
		if (otaStatusText.value === 'ota未连接') return 'status-disconnected'
		return 'status-pending'
	})

	const wsStatusClass = computed(() => {
		if (wsStatusText.value === 'ws已连接') return 'status-success'
		if (wsStatusText.value === 'ws已断开') return 'status-disconnected'
		return 'status-pending'
	})

	// 方法定义
	const switchTab = (tab) => {
		activeTab.value = tab
	}

	const toggleConfig = () => {
		configPanelExpanded.value = !configPanelExpanded.value
	}

	const log = (message, type = 'info') => {
		const now = new Date()
		const timestamp = `[${now.toLocaleTimeString()}.${now.getMilliseconds().toString().padStart(3, '0')}]`

		logs.value.push({
			timestamp,
			message,
			type
		})

		// 限制日志数量
		if (logs.value.length > 100) {
			logs.value.shift()
		}

		// 滚动到底部
		uni.pageScrollTo({
			scrollTop: 999999,
			duration: 0
		})
	}

	const generateRandomMac = () => {
		const hexDigits = '0123456789ABCDEF'
		let mac = ''
		for (let i = 0; i < 6; i++) {
			if (i > 0) mac += ':'
			for (let j = 0; j < 2; j++) {
				mac += hexDigits.charAt(Math.floor(Math.random() * 16))
			}
		}
		return mac
	}

	const getConfig = () => {
		return {
			deviceId: deviceMac.value,
			deviceName: deviceName.value,
			deviceMac: deviceMac.value,
			clientId: clientId.value,
			token: token.value
		}
	}

	const validateConfig = (config) => {
		if (!config.deviceMac) {
			log('设备MAC地址不能为空', 'error')
			return false
		}
		if (!config.clientId) {
			log('客户端ID不能为空', 'error')
			return false
		}
		return true
	}

	const connectToServer = async () => {

		// OTA检查
		const url = serverUrl.value.trim()
		const config = getConfig()

		try {
			const otaResponse = await uni.request({
				url: otaUrl.value,
				method: 'POST',
				header: {
					'Content-Type': 'application/json',
					'Device-Id': config.deviceId,
					'Client-Id': config.clientId
				},
				data: JSON.stringify({
					version: 0,
					uuid: "",
					application: {
						name: "dahua",
						version: "1.1.0",
						compile_time: "2025-04-16 10:00:00",
						idf_version: "4.4.3",
						elf_sha256: "1234567890abcdef1234567890abcdef1234567890abcdef"
					},
					ota: {
						label: "dahua"
					},
					board: {
						type: "dahua",
						ssid: "dahua",
						rssi: 0,
						channel: 0,
						ip: "192.168.1.1",
						mac: config.deviceMac
					},
					flash_size: 0,
					minimum_free_heap_size: 0,
					mac_address: config.deviceMac,
					chip_model_name: "",
					chip_info: {
						model: 0,
						cores: 0,
						revision: 0,
						features: 0
					},
					partition_table: [{
						label: "",
						type: 0,
						subtype: 0,
						address: 0,
						size: 0
					}]
				})
			})

			if (otaResponse.statusCode !== 200) {
				throw new Error(`OTA检查失败: ${otaResponse.statusCode}`)
			}
		} catch (error) {}
		try {

			// 在UniApp中使用WebSocket
			websocket.value = uni.connectSocket({
				url: url + '?device-id=' + config.deviceMac + '&client-id=' + config.clientId,
				success: () => {},
				fail: (err) => {
					throw new Error(`连接失败: ${err.errMsg}`)
				}
			})


			// 监听WebSocket事件
			websocket.value.onOpen((res) => {
				log(`已连接到服务器: ${url}`, 'success')
				connectButtonText.value = '断开'
				isConnected.value = true
				sendHelloMessage()
			})

			websocket.value.onClose(() => {
				log('已断开连接', 'info')
				connectButtonText.value = '连接'
				isConnected.value = false
			})

			websocket.value.onError((err) => {
				log(`WebSocket错误: ${err.errMsg}`, 'error')
			})

			websocket.value.onMessage((event) => {
				try {
					if (typeof event.data === 'string') {
						const message = JSON.parse(event.data)

						if (message.type === 'hello') {
							log(`服务器回应：${JSON.stringify(message, null, 2)}`, 'success')
						} else if (message.type === 'tts') {
							if (message.state === 'start') {
								log('服务器开始发送语音', 'info')
							} else if (message.state === 'sentence_start') {
								log(`服务器发送语音段: ${message.text}`, 'info')
								if (message.text) {
									messages.value.push({
										text: message.text,
										isUser: false
									})
								}
							} else if (message.state === 'sentence_end') {
								log(`语音段结束: ${message.text}`, 'info')
							} else if (message.state === 'stop') {
								log('服务器语音传输结束', 'info')
							}
						} else if (message.type === 'stt') {
							log(`识别结果: ${message.text}`, 'info')
							messages.value.push({
								text: `[语音识别] ${message.text}`,
								isUser: true
							})
						} else if (message.type === 'llm') {
							log(`大模型回复: ${message.text}`, 'info')
							if (message.text && message.text !== '😊') {
								messages.value.push({
									text: message.text,
									isUser: false
								})
							}
						} else if (message.type === 'mcp') {
							log(`服务器下发: ${JSON.stringify(message)}`, 'info')
							// 处理MCP消息
						} else {
							log(`未知消息类型: ${message.type}`, 'info')
							messages.value.push({
								text: JSON.stringify(message, null, 2),
								isUser: false
							})
						}
					} else {
						// 处理二进制数据    event.data就是服务器下发的音频数据
						handleBinaryMessage(event.data)
					}
				} catch (error) {
					log(`WebSocket消息处理错误: ${error.message}`, 'error')
				}
			})
		} catch (error) {
			log(`连接错误: ${error.message}`, 'error')
			wsStatusText.value = 'ws未连接'
		}
	}
	// 发送hello握手消息
	const sendHelloMessage = async () => {
		if (!isConnected.value) return
		try {
			const config = getConfig()
			const helloMessage = {
				type: 'hello',
				device_id: config.deviceId,
				device_name: config.deviceName,
				device_mac: config.deviceMac,
				token: config.token,
				features: {
					mcp: true
				}
			}
			websocket.value.send({
				data: JSON.stringify(helloMessage),
				success: () => {
					// 等待服务器响应
					return new Promise(resolve => {
						const timeout = setTimeout(() => {
							resolve(false)
						}, 5000)
	
						// 临时监听一次消息，接收hello响应
						const onMessageHandler = (res) => {
							try {
								const response = JSON.parse(res.data)
								if (response.type === 'hello' && response.session_id) {
									log(`服务器握手成功，会话ID: ${response.session_id}`,
										'success')
									clearTimeout(timeout)
									websocket.value.offMessage(onMessageHandler)
									resolve(true)
								}
							} catch (e) {
								// 忽略非JSON消息
							}
						}
	
						websocket.value.onMessage(onMessageHandler)
					})
				},
				fail: (err) => {
					log(`发送hello消息错误: ${err.errMsg}`, 'error')
					return false
				}
			})
		} catch (error) {
			log(`发送hello消息错误: ${error.message}`, 'error')
			return false
		}
	}

	const disconnectFromServer = () => {
		if (websocket) {
			websocket.close()
		}
		isRecording.value = false
	}

	const toggleConnection = () => {
		if (isConnected.value) {
			disconnectFromServer()
		} else {
			connectToServer()
		}
	}

	const sendTextMessage = () => {
		const message = messageInput.value.trim()
		if (!message || !websocket) return

		try {
			const listenMessage = {
				type: 'listen',
				mode: 'manual',
				state: 'detect',
				text: message
			}

			websocket.value.send({
				data: JSON.stringify(listenMessage),
			})
			messages.value.push({
				text: message,
				isUser: true
			})
			log(`发送文本消息: ${message}`, 'info')
			messageInput.value = ''
		} catch (error) {
			log(`发送消息错误: ${error.message}`, 'error')
		}
	}

	const startRecording = () => {
		if (isRecording.value) return

		isRecording.value = true
		recordingTime.value = 0

		recordingTimer.value = setInterval(() => {
			recordingTime.value += 0.1
		}, 100)

		// 发送开始录音消息
		if (websocket) {
			const listenMessage = {
				type: 'listen',
				mode: 'manual',
				state: 'start'
			}

			websocket.value.send({
				data: JSON.stringify(listenMessage),
			})
			log('开始录音', 'info')
		}
	}

	const stopRecording = () => {
		if (!isRecording.value) return

		isRecording.value = false
		clearInterval(recordingTimer.value)

		// 发送停止录音消息
		if (websocket ) {
			const listenMessage = {
				type: 'listen',
				mode: 'manual',
				state: 'stop'
			}

			websocket.value.send({
				data: JSON.stringify(listenMessage),
			})
			log('停止录音', 'info')
		}
	}

	const toggleRecording = () => {
		if (isRecording.value) {
			stopRecording()
		} else {
			startRecording()
		}
	}

	const handleBinaryMessage = (data) => {
		try {
			let arrayBuffer

			if (data instanceof ArrayBuffer) {
				arrayBuffer = data
				log(`收到ArrayBuffer音频数据，大小: ${data.byteLength}字节`, 'debug')
			} else {
				log(`收到未知类型的二进制数据: ${typeof data}`, 'warning')
				return
			}

			// 处理音频数据
			const opusData = new Uint8Array(arrayBuffer)
			if (opusData.length > 0) {
				log(`收到音频数据，大小: ${opusData.length}字节`, 'debug')
			}
		} catch (error) {
			log(`处理二进制消息出错: ${error.message}`, 'error')
		}
	}

	const testAuthentication = () => {
		log('开始测试认证...', 'info')
		// 认证测试逻辑
	}

	// 监听配置变化
	watch([deviceMac, clientId], () => {
		displayMac.value = deviceMac.value
		displayClient.value = clientId.value
	})

	// 页面加载
	onLoad(() => {
		// 从本地存储加载配置
		deviceMac.value = '00:11:22:33:44:55'

		displayMac.value = deviceMac.value
		displayClient.value = clientId.value

		const savedOtaUrl = uni.getStorageSync('otaUrl')
		if (savedOtaUrl) {
			otaUrl.value = savedOtaUrl
		}

		const savedWsUrl = uni.getStorageSync('wsUrl')
		if (savedWsUrl) {
			serverUrl.value = savedWsUrl
		}

		log('准备就绪，请连接服务器开始测试...', 'info')
	})

	// 页面卸载时断开连接
	onMounted(() => {
		// 页面挂载后的初始化逻辑
	})
</script>

<style scoped>
	.container {
		padding: 20rpx;
		background-color: #f5f5f5;
	}

	.header {
		text-align: center;
		margin-bottom: 30rpx;
	}

	.title {
		font-size: 26rpx;
		color: #333;
		font-weight: bold;
	}

	.section {
		background-color: #fff;
		border-radius: 10rpx;
		padding: 10rpx;
		margin-bottom: 20rpx;
		box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
	}

	.section-header {
		display: flex;
		align-items: center;
		flex-wrap: wrap;
		gap: 20rpx;
		margin-bottom: 10rpx;
	}

	.section-title {
		font-size: 22rpx;
		color: #444;
		font-weight: bold;
	}

	.device-info {
		display: flex;
		flex-direction: column;
		font-size: 20rpx;
		color: #666;
	}

	.device-info text {
		margin-bottom: 10rpx;
	}

	.toggle-button {
		margin-left: auto;
		padding: 10rpx 20rpx;
		background-color: #4285f4;
		color: white;
		border-radius: 8rpx;
		font-size: 20rpx;
	}

	.config-panel {
		margin-top: 20rpx;
	}

	.config-item {
		display: flex;
		align-items: center;
		margin-bottom: 20rpx;
	}

	.config-label {
		width: 160rpx;
		font-size: 28rpx;
		color: #666;
	}

	.config-input {
		flex: 1;
		padding: 15rpx;
		border: 1rpx solid #ddd;
		border-radius: 10rpx;
		font-size: 28rpx;
	}

	.connection-status {
		display: flex;
		flex-direction: column;
		font-size: 24rpx;
		color: #666;
	}

	.connection-status text {
		margin-bottom: 10rpx;
	}

	.status-success {
		color: green;
	}

	.status-disconnected {
		color: red;
	}

	.status-pending {
		color: orange;
	}

	.connection-controls {
		display: flex;
		flex-direction: column;
		gap: 20rpx;
	}

	.url-input {
		padding: 15rpx;
		border: 1rpx solid #ddd;
		border-radius: 10rpx;
		font-size: 20rpx;
	}

	.control-button {
		padding: 10rpx;
		background-color: #4285f4;
		color: white;
		border-radius: 10rpx;
		font-size: 20rpx;
	}

	.tabs {
		display: flex;
		margin-bottom: 20rpx;
	}

	.tab {
		flex: 1;
		padding: 10rpx;
		background-color: #f0f0f0;
		border: none;
		font-size: 20rpx;
		color: #666;
	}

	.tab.active {
		background-color: #4285f4;
		color: white;
		font-weight: bold;
	}

	.message-input {
		display: flex;
		gap: 20rpx;
	}

	.message-input-field {
		flex: 1;
		padding: 15rpx;
		border: 1rpx solid #ddd;
		border-radius: 10rpx;
		font-size: 28rpx;
	}

	.send-button {
		padding: 15rpx 30rpx;
		background-color: #4285f4;
		color: white;
		border-radius: 10rpx;
		font-size: 28rpx;
	}

	.audio-controls {
		display: flex;
		justify-content: center;
		margin-bottom: 20rpx;
	}

	.record-button {
		padding: 20rpx 40rpx;
		background-color: #db4437;
		color: white;
		border-radius: 10rpx;
		font-size: 28rpx;
	}

	.record-button.recording {
		animation: pulse 1.5s infinite;
	}

	@keyframes pulse {
		0% {
			background-color: #db4437;
		}

		50% {
			background-color: #ff6659;
		}

		100% {
			background-color: #db4437;
		}
	}

	.audio-visualizer {
		height: 120rpx;
		border: 1rpx solid #ddd;
		border-radius: 10rpx;
		background-color: #fafafa;
	}

	.visualizer-canvas {
		width: 100%;
		height: 100%;
	}

	.content-container {
		display: flex;
		flex-direction: row;
		height: 600rpx;
	}

	.conversation-container {
		flex: 1;
		margin-right: 20rpx;
	}

	.conversation {
		height: 100%;
		border: 1rpx solid #ddd;
		border-radius: 10rpx;
		padding: 20rpx;
		background-color: white;
	}

	.message {
		margin-bottom: 20rpx;
		padding: 16rpx 24rpx;
		border-radius: 16rpx;
		max-width: 80%;
	}

	.user {
		background-color: #e2f2ff;
		margin-left: auto;
		text-align: right;
	}

	.server {
		background-color: #f0f0f0;
		margin-right: auto;
	}

	.log-container {
		flex: 1;
		padding: 20rpx;
		background-color: #f0f0f0;
		border-radius: 10rpx;
		font-family: monospace;
		overflow-y: scroll;
	}

	.log-entry {
		margin: 10rpx 0;
		font-size: 24rpx;
	}

	.log-info {
		color: #333;
	}

	.log-error {
		color: #db4437;
	}

	.log-success {
		color: #0f9d58;
	}

	.log-warning {
		color: #f4b400;
	}

	.log-debug {
		color: #999;
	}
</style>